/*
 * FunctionAnalyzer.cpp
 *
 *  Created on: 27 мая 2022 г.
 *      Author: nikita
 */

#include "FunctionAnalyzer.h"

namespace STC_calc {

const std::map<std::string, Enums::brackets_types> FunctionAnalyzer::open_brackets
{
	{"(", Enums::standart},
	//{"{", Enums::figure},
	{"[", Enums::square},
};

const std::map<std::string, Enums::brackets_types> FunctionAnalyzer::close_brackets
{
	{")", Enums::standart},
	//{"}", Enums::figure},
	{"]", Enums::square}
};

const std::map<std::string, int> FunctionAnalyzer::priority_function
{
	{"", -1},
	{"sin", 100},
	{"cos", 100},
	{"tg", 100},
	{"ctg", 100},
	{"log", 100},
	{"ln", 100},
	{"lg", 100},
	{"pow", 200},
	{"^", 200},
	{"div", 300},
	{"/", 300},
	{"mult", 300},
	{"*", 300},
	{"sum", 400},
	{"+", 400},
	{"diff", 400},
	{"-", 400},
	{"set", 10000}
};

const std::map<std::string, std::string> FunctionAnalyzer::function_dictionary
{
	{"set", "set"},
	{"sum", "+"}, {"+","sum"},
	{"diff", "-"}, {"-","diff"},
	{"mult", "*"}, {"*","mult"},
	{"div", "/"}, {"/","div"},
	{"pow", "^"}, {"^","pow"},
	{"sin", "sin"},
	{"cos", "cos"},
	{"tg", "tg"},
	{"ctg", "ctg"},
	{"log", "log"},
	{"ln", "ln"},
	{"lg", "lg"}
};

const string_vector FunctionAnalyzer::base_functions
{//Functions that have a base(in classical math only logarithm)
	{"log"}
};

std::string FunctionAnalyzer::base_define(const std::string& str, const std::string& func)
{//Find base of functions like the logarithm. str is a whole equation, func is function.
	int pos = str.find(";");
	if (str.find(";") == std::string::npos) throw(RuntimeErrorClass("Error: Function has no base"));

	//Function is finding the ';' symbol. Number before that is considered as a base
	const std::string arg = str.substr(func.length()+1, pos-func.length()-1);//without bracket and ';'
	return arg;
}

	void FunctionAnalyzer::remove_base_garbage(std::string& str)
	{//remove base function, it's base and brackets
		str = str.substr(str.find(";")+1);
		str.erase(str.length()-1, 1);
	}

bool FunctionAnalyzer::has_base(const std::string& func_name)
{//checks if it's a base function
	for(auto str : base_functions)
	{
		if (str == func_name) return true;
	}
	return false;
}

int FunctionAnalyzer::find_without_brackets(const std::string& str, const std::string& substr)
{//finding substring that there are not in brackets
	Enums::brackets_types type = Enums::not_a_bracket;
	bool is_bracket = false;

	if (str.length() < substr.length()) return -1;

	int i = 0;
	do
	{
		std::string sym = str.substr(i, 1);
		if (!is_bracket&&(is_open_bracket(sym)))
		{
			is_bracket = true;
			type = define_open_bracket_type(sym);
		}
		if (is_bracket&&(is_it_close_bracket(sym,type)))
		{
			is_bracket = false;
			type = Enums::not_a_bracket;
		}
		if (!is_bracket&&(str.substr(i, substr.length()) == substr)) return i;
		i++;
	}
	while(i <= (str.length() - substr.length()));
	return -1;
}

string_vector FunctionAnalyzer::define_func_args(const std::string& args, const std::string& func_name)
{//return function args
	std::string str = args;
	std::string substr = function_dictionary.at(func_name);
	string_vector ret(0);

	if (has_base(substr))
	{
		ret.push_back(base_define(str, substr));
		remove_base_garbage(str);
	}
	int l_pos = 0;
	do
	{
		int pos = find_without_brackets(str, substr);
		if (pos == -1) pos = str.length()-1;

		const std::string arg = str.substr(0, pos);

		l_pos = pos+substr.length();
		if(l_pos > str.length()) break;
		str = str.substr(l_pos);

		if (arg.length() != 0) ret.push_back(arg);

	}
	while(find_without_brackets(str, substr) != -1);

	if (str.length() > 0)ret.push_back(str);

	return ret;
}


void FunctionAnalyzer::remove_brackets(std::string& str)
{
	const int last_sym = str.length()-1;
	if ((str.substr(0,1) == "(")&&(str.substr(last_sym,1) == ")"))
	{
		str.erase(last_sym, 1);
		str.erase(0, 1);
		remove_brackets(str);
	}
	return;
}

std::string FunctionAnalyzer::define_name_function(const std::string& str)
{
	std::string simp_str = simplify_string(str);
	std::string ret = "";
	std::string long_function = "";
	for (auto a : simp_str)
	{
		long_function+=std::string(1, a);

		if (function_dictionary.find(long_function) == function_dictionary.end()) continue;

		std::string temp = function_dictionary.at(long_function);
		if (priority_function.at(temp) > priority_function.at(ret)) ret = temp;

		long_function = "";
	}
	if (ret == "") ret = "set";
	return ret;
}

bool FunctionAnalyzer::is_open_bracket(const std::string& s)
{
	return (open_brackets.find(s) != open_brackets.end());
}

bool FunctionAnalyzer::is_it_close_bracket(const std::string& s, const Enums::brackets_types type)
{
	if (close_brackets.find(s) == close_brackets.end()) return false;

	if (close_brackets.at(s) == type) return true;

	return false;
}

std::string FunctionAnalyzer::simplify_string(const std::string str)
{//Remove everything that could prevent the definition of type of the operation
//brackets, numbers, and stuff. 2+46-g*21*(***) -> +-**
	bool is_bracket_open = false;
	std::string ret = "";
	std::string add_str = "";

	Enums::brackets_types type_open = Enums::not_a_bracket;

	for (char a: str)
	{
		std::string s(1, a);
		if (!is_bracket_open) is_bracket_open = is_open_bracket(s);
		if (is_bracket_open && type_open == Enums::not_a_bracket) type_open = define_open_bracket_type(s);

		if (is_it_close_bracket(s, type_open))
		{
			is_bracket_open = false;
			type_open = Enums::not_a_bracket;
			//ret+="(ARG)";
			continue;
		}

		if (is_bracket_open) continue;

		add_str+=s;

		if (function_dictionary.find(s) != function_dictionary.end())
		{//small functions like +, - or *
			ret+=s;
			add_str = "";
			continue;
		}

		if (function_dictionary.find(add_str) != function_dictionary.end())
		{//functions like log, cos or sin
			ret+=add_str;
			add_str="";
			continue;
		}
	}
	return ret;
}

Enums::brackets_types FunctionAnalyzer::define_open_bracket_type(const std::string& s)
{
	if (open_brackets.find(s) == open_brackets.end()) return Enums::not_a_bracket;

	return open_brackets.at(s);
}

} /* namespace STC_calc */
